// Copyright (C) 2017 Gabriel Gouvine - All Rights Reserved

#pragma once

#include <cstddef>
#include <cstdint>
#include <boost/range/iterator_range_core.hpp>
#include <boost/numeric/ublas/matrix.hpp>

namespace minipart {

typedef std::uint32_t Index;
typedef std::int32_t Weight;
typedef std::int32_t Resource;

template <typename T>
using Matrix = boost::numeric::ublas::matrix<T>;

// Typesafe handles: Node/Edge/Part

struct Node {
  Index id;

  Node () : id(-1) {}
  explicit Node (Index i) : id(i) {}
  bool operator==(Node o) const { return id == o.id; }
  bool operator!=(Node o) const { return id != o.id; }
  bool operator<(Node o) const { return id < o.id; }
};

struct Part {
  std::uint8_t id;

  Part () : id(-1) {}
  explicit Part (Index i) : id(i) {}
  bool operator==(Part o) const { return id == o.id; }
  bool operator!=(Part o) const { return id != o.id; }
  bool operator<(Part o) const { return id < o.id; }
};

struct Edge {
  Index id;

  Edge () : id(-1) {}
  explicit Edge (Index i) : id(i) {}
  bool operator==(Edge o) const { return id == o.id; }
  bool operator!=(Edge o) const { return id != o.id; }
  bool operator<(Edge o) const { return id < o.id; }
};

struct Edge2 {
  Index id;

  Edge2 () : id(-1) {}
  explicit Edge2 (Index i) : id(i) {}
  bool operator==(Edge2 o) const { return id == o.id; }
  bool operator!=(Edge2 o) const { return id != o.id; }
  bool operator<(Edge2 o) const { return id < o.id; }
};

// Node/Edge iterators
template<class T>
struct ObjIt : T, boost::iterator_facade<ObjIt<T>, T, boost::forward_traversal_tag> {
  typedef T reference;
  ObjIt(T i) : T(i) {}
  void increment() { ++this->id; }
  T operator*() const { return *this; }
};

template <class T> using Range = boost::iterator_range<ObjIt<T> >;
template <class T> using SliceIt = typename std::vector<T>::const_iterator;  
template <class T> using Slice = boost::iterator_range<SliceIt<T> >;  

class Mapping {
 public:
  Mapping(std::size_t s=0) : m_(s, Part(0)) {}
  std::size_t nNodes() const { return m_.size(); }

  Part  operator[](Node n) const { return m_[n.id]; }
  Part& operator[](Node n)       { return m_[n.id]; }

 private:
  std::vector<Part> m_;
};

}  // End namespace minipart
